/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2006, University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.classfile;

import java.io.Serializable;
import java.util.regex.Pattern;

import org.apache.bcel.classfile.JavaClass;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
import edu.umd.cs.findbugs.util.ClassName;

/**
 * Descriptor identifying a class.
 * 
 * @author David Hovemeyer
 */
public class ClassDescriptor implements Comparable<ClassDescriptor>, Serializable {
	private static final long serialVersionUID = 1L;
	private final String className;
	private static final Pattern ANONYMOUS_CLASS_NAME = Pattern.compile(".*\\$[0-9]*$");

	public static final ClassDescriptor[] EMPTY_ARRAY = new ClassDescriptor[0];
	/**
	 * Constructor.
	 * 
	 * @param className class name in VM format, e.g. "java/lang/String"
	 */
	 protected ClassDescriptor(@SlashedClassName String className) {
		if (className.indexOf('.') >= 0) {
			throw new IllegalArgumentException("Class name " + className + " not in VM format");
		}
		if (!ClassName.isValidClassName(className)) {
			throw new IllegalArgumentException("Invalid class name " + className);
		}
		this.className = className;
	}

	/**
	 * @return Returns the class name in VM format, e.g. "java/lang/String"
	 */
	public final @SlashedClassName String getClassName() {
		return className;
	}

	/* (non-Javadoc)
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(ClassDescriptor o) {
		return className.compareTo(o.className);
	}

	/**
	 * Get the resource name of this class as it would appear in the classpath.
	 * E.g., "java/lang/String.class"
	 * 
	 * @return the resource name
	 */
	public String toResourceName() {
		return className + ".class";
	}

	/**
	 * Get the name of the class in dotted format.
	 * 
	 * @return the name of the class in dotted format
	 */
	public @DottedClassName String toDottedClassName() {
		return ClassName.toDottedClassName(className);
	}
	/**
	 * Get the name of the class in dotted format.
	 * 
	 * @return the name of the class in dotted format
	 */
	public  @DottedClassName  String getDottedClassName() {
		return ClassName.toDottedClassName(className);
	}

	/**
	 * Get the name of the package in dotted format.
	 * 
	 * @return the name of the package in dotted format
	 */
	public  @DottedClassName  String getPackageName() {
		return ClassName.extractPackageName(ClassName.toDottedClassName(className));
	}

	/**
	 * Get the simple name of the class 
	 * 
	 * @return the simple name of the class
	 */
	public   String getSimpleName() {
		return ClassName.extractSimpleName(ClassName.toDottedClassName(className));
	}
	/**
     * Create a class descriptor from a resource name.
     * 
     * @param resourceName the resource name
     * @return the class descriptor
     * @deprecated Use {@link DescriptorFactory#createClassDescriptorFromResourceName(String)} instead
     */
    @Deprecated
    public static ClassDescriptor fromResourceName(String resourceName) {
        return DescriptorFactory.createClassDescriptorFromResourceName(resourceName);
    }
	
	/**
     * Create a class descriptor from a field signature
     * @deprecated Use {@link DescriptorFactory#createClassDescriptorFromFieldSignature(String)} instead
     * 
     */
    @Deprecated
    public static @CheckForNull ClassDescriptor fromFieldSignature(String signature) {
        return DescriptorFactory.createClassDescriptorFromFieldSignature(signature);
    }

	/**
     * Determine whether or not the given resource name refers to a class.
     * 
     * @param resourceName the resource name
     * @return true if the resource is a class, false otherwise
     * @deprecated Use {@link DescriptorFactory#isClassResource(String)} instead
     */
    @Deprecated
    public static boolean isClassResource(String resourceName) {
        return DescriptorFactory.isClassResource(resourceName);
    }

	/**
     * @deprecated Use {@link DescriptorFactory#createClassDescriptorFromSignature(String)} instead
     */
    @Deprecated
    public static ClassDescriptor createClassDescriptorFromSignature(String signature) {
        return DescriptorFactory.createClassDescriptorFromSignature(signature);
    }
	/**
     * @deprecated Use {@link DescriptorFactory#createClassDescriptor(String)} instead
     */
    @Deprecated
    public static ClassDescriptor createClassDescriptor(@SlashedClassName String className) {
        return DescriptorFactory.createClassDescriptor(className);
    }
	/**
     * @deprecated Use {@link DescriptorFactory#createClassDescriptor(String[])} instead
     */
    @Deprecated
    public static ClassDescriptor[] createClassDescriptor(String[] classNames) {
        return DescriptorFactory.createClassDescriptor(classNames);
    }
	/**
     * @deprecated Use {@link DescriptorFactory#createClassDescriptorFromDottedClassName(String)} instead
     */
    @Deprecated
    public static ClassDescriptor createClassDescriptorFromDottedClassName(String dottedClassName) {
        return DescriptorFactory.createClassDescriptorFromDottedClassName(dottedClassName);
    }
	/**
     * @deprecated Use {@link DescriptorFactory#createClassDescriptor(JavaClass)} instead
     */
    @Deprecated
    public static ClassDescriptor createClassDescriptor(JavaClass c) {
        return DescriptorFactory.createClassDescriptor(c);
    }
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return className;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public final boolean equals(Object obj) {
		if (obj == null || !(obj instanceof ClassDescriptor)) {
			return false;
		}
		
		// All instances of ClassDescriptor should be considered
		// equal if they represent the same class,
		// even if compared to an object of a different runtime class.
		return getClassName().equals(((ClassDescriptor)obj).getClassName());
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		return className.hashCode();
	}

	/**
     * Throw a ClassNotFoundException to indicate that class named
     * by given ClassDescriptor cannot be found.
     * The exception message is formatted in a way that can
     * be decoded by ClassNotFoundExceptionParser.
     * 
     * @param classDescriptor ClassDescriptor naming a class that cannot be found
     * @throws ClassNotFoundException
     * @see edu.umd.cs.findbugs.ba.ClassNotFoundExceptionParser
     */
    public static void throwClassNotFoundException(ClassDescriptor classDescriptor) throws ClassNotFoundException {
    	throw new ClassNotFoundException("Class " + classDescriptor.toDottedClassName() + " cannot be resolved");
    }
    
    public boolean isAnonymousClass() {
    	return ANONYMOUS_CLASS_NAME.matcher(className).matches();
    }
}
